/* Copyright (C) 2006, 2007 by Daniel Gheorghian
 All rights reserved

 Use and distribute freely, except: don't remove my name from the source or documentation
 (don't take credit for my work), mark your changes (don't get me blamed for your possible bugs),
 don't alter or remove this notice.

 No warrantee of any kind, express or implied, is included with this software; use at your
 own risk, responsibility for damages (if any) to anyone resulting from the use of this
 software rests entirely with the user.

 Send bug reports, bug fixes, enhancements, requests, flames, etc., and I'll try to keep
 a version up to date.  I can be reached at: daniel.gheorghian@gmail.com
*/
package com.ds.asm;

import com.ds.asm.instruction.*;
import com.ds.asm.parser.ASMParser;
import com.ds.components.bars.ElbowBar;
import com.ds.components.builders.PanelBarPanelBuilder;
import com.ds.components.composite.PanelBar;
import com.ds.components.JXButton;
import com.ds.renderer.JXButtonRenderer;
import jasmin.gui.JasDocument;
import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.layout.graph.NodeLinkTreeLayout;
import prefuse.activity.Activity;
import prefuse.controls.PanControl;
import prefuse.controls.SubtreeDragControl;
import prefuse.controls.WheelZoomControl;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.render.DefaultRendererFactory;
import prefuse.util.ColorLib;
import prefuse.visual.VisualItem;

import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Map;
import java.util.Stack;

public class MainFrame extends JPanel
{
	private PanelBar graphDisplay;
	private Graph graph;
	private Display display;
	private Visualization vis;
	private JXButton loadFile;
	private JXButton generate;

	public MainFrame()
	{
		super();

		this.setLayout( new BorderLayout( 1, 10 ) );
		this.setBackground( Color.black );

		initGraph();
		initGraphDisplay();
		initActions();

		this.add( this.graphDisplay.getMainContainer(), BorderLayout.CENTER );

		try
		{
			loadFile( new File( "test.asm" ) );
		}
		catch (IOException ex)
		{
			ex.printStackTrace();
		}
	}

	private void loadFile( File f ) throws IOException
	{
		JasDocument doc = new JasDocument();

		ASMParser parser = new ASMParser();
		Instruction instr;
		Instruction instr1 = null;
		Instruction instr2 = null;
		Stack<Instruction> s = new Stack<Instruction>();
		Map<Instruction, Boolean> visited = new Hashtable<Instruction, Boolean>();
		Map<String, Node> labels = new Hashtable<String, Node>();
		Map<Node, String> references = new Hashtable<Node, String>();
		Node cell1 = null;
		Node cell2 = null;

		doc.loadFile( f );

//		while ( doc.hasMoreInstructions() )
//		{
//			System.out.println( doc.getCurrentLine() );
//			doc.step();
//		}

//		System.out.println(doc.getEAXValue());

		instr = parser.parse( f );
		s.push( instr );

		while( !s.empty() )
		{
			instr = s.pop();

			if( instr == null )
			{
				cell1 = null;
				continue;
			}

			if( visited.get( instr ) != null )
			{
				continue;
			}

			visited.put( instr, true );
			cell2 = graph.addNode();
			cell2.setString( "name", "   " + instr.getInstruction() + "   ");
			instr2 = instr;

			if( instr instanceof LABEL)
			{
				labels.put( instr.getInstruction(), cell2 );
			}

			if( cell1 != null )
			{
				if( instr1 instanceof JMP)
				{
					String i = instr1.getInstruction();
					String label = i.substring( i.indexOf( " " ) ).trim() + ":";
					references.put( cell1, label );
				}
				else
				{
					if( instr1 instanceof SingleInputDoubleOutputInstruction)
					{
						String i = instr1.getInstruction();
						String label = i.substring( i.indexOf( " " ) ).trim() + ":";
						references.put( cell1, label );
					}
					graph.addEdge( cell1, cell2 );
				}
			}

			if( instr instanceof SingleInputOutputInstruction)
			{
				s.push( ((SingleInputOutputInstruction)instr).getOutput() );
			}
			else
			{
				if( instr instanceof SingleInputDoubleOutputInstruction)
				{
					s.push( ((SingleInputDoubleOutputInstruction)instr).getOutputThen() );
					s.push( ((SingleInputDoubleOutputInstruction)instr).getOutputElse() );
				}
				else
				{
					if( instr instanceof MultiInputSingleOutputInstruction)
					{
						s.push( ((MultiInputSingleOutputInstruction)instr).getOutput() );
					}
				}
			}

			cell1 = cell2;
			instr1 = instr2;
		}

		for( Node i : references.keySet() )
		{
			graph.addEdge( i, labels.get( references.get( i ) ) );
		}

		vis.run("color");
		vis.run("layout");
	}

	private void initGraphDisplay()
	{
		JPanel contents = new JPanel( new BorderLayout() );
		contents.add( display, BorderLayout.CENTER );

		PanelBarPanelBuilder builder = new PanelBarPanelBuilder();
		builder.elbowBarBuilder.setConnectorSize( 170 );
		builder.elbowBarBuilder.setConnectorPosition( ElbowBar.RIGHT_CONNECTOR );
		builder.elbowBarBuilder.setRightSpace( 450 );
		builder.contentsPanel = contents;

		this.graphDisplay = builder.build();
		this.graphDisplay.topElbowBar.setText( "Structural Testing" );
		this.graphDisplay.topElbowBar.setTextBackground( Color.black );
		this.graphDisplay.topElbowBar.setForeground( Color.white );
		this.graphDisplay.topElbowBar.setFont( new Font( "Arial Black", Font.PLAIN, 30 ) );
		this.graphDisplay.topElbowBar.setPreferredSize( new Dimension( 900, 75 ) );
	}

	private void initGraph()
	{
		graph = new Graph( true );
		graph.getNodeTable().addColumn( "name", String.class );
		vis = new Visualization();
		vis.add("graph", graph);

		MyEdgeRenderer renderer = new MyEdgeRenderer( Constants.EDGE_TYPE_CURVE, Constants.EDGE_ARROW_FORWARD );
		renderer.setArrowHeadSize( 5, 5 );

		BulletRenderer r = new BulletRenderer("name");
		r.setHorizontalAlignment( Constants.CENTER );
		r.setVerticalAlignment( Constants.CENTER );

		vis.setRendererFactory(new DefaultRendererFactory(r,renderer));

		ColorAction fill = new ColorAction("graph.nodes", VisualItem.FILLCOLOR, ColorLib.rgb(255,180,180));
		ColorAction text = new ColorAction("graph.nodes", VisualItem.TEXTCOLOR, ColorLib.gray(0));
		ColorAction border = new ColorAction( "graph.nodes", VisualItem.STROKECOLOR,ColorLib.gray( 120, 150 ) );
		ColorAction edges = new ColorAction("graph.edges", VisualItem.STROKECOLOR, ColorLib.setAlpha( ColorLib.color( Color.orange ), 150 ) );
		ColorAction fillEdges = new ColorAction("graph.edges", VisualItem.FILLCOLOR, ColorLib.setAlpha( ColorLib.color( Color.orange ), 150 ) );

		ActionList color = new ActionList();
		color.add(fill);
		color.add(text);
		color.add(edges);
		color.add(fillEdges);
		color.add( border );

		ActionList layout = new ActionList(Activity.DEFAULT_STEP_TIME);
		layout.add( new NodeLinkTreeLayout( "graph", Constants.ORIENT_TOP_BOTTOM, 15, 10, 10 ) );
		layout.add(new RepaintAction());

		vis.putAction("color", color);
		vis.putAction("layout", layout);

		display = new Display(vis);
		display.setBackground( Color.black );
		display.setPreferredSize( new Dimension( 400, 700 ) );
		display.addControlListener(new PanControl() );
		display.addControlListener( new WheelZoomControl() );
		display.addControlListener( new SubtreeDragControl() );
	}

	private void initActions()
	{
		loadFile = new JXButton();
		loadFile.setText( "Load File..." );
		loadFile.setRenderer( new JXButtonRenderer() );
		loadFile.setPreferredSize( new Dimension( 170, 40 ) );
//		loadFile.addActionListener( this.loadFileAction );

		generate = new JXButton();
		generate.setText( "Generate" );
		generate.setRenderer( new JXButtonRenderer() );
		generate.setPreferredSize( new Dimension( 170, 40 ) );

		graphDisplay.elbowPanel.add( loadFile );
		graphDisplay.elbowPanel.add( generate );
	}

}
